﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Collections;

namespace StationUpdate
{
    public class Ping
    {
        public class IcmpPacket
        {
            public Byte Type;		// type of message
            public Byte SubCode;    // type of sub code
            public UInt16 CheckSum;   // ones complement checksum of struct
            public UInt16 Identifier;      // identifier
            public UInt16 SequenceNumber;     // sequence number  
            public Byte[] Data;
        }

        const int SOCKET_ERROR = -1;
        const int ICMP_ECHO = 8;

        public Ping()
        {

        }

        /// <summary>
        /// 测试指定的IP是否连通
        /// </summary>
        /// <param name="ip"></param>
        /// <returns></returns>

        public static bool PingHost(string ip)
        {
            Socket socket=null;
            try
            {
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);

                IPEndPoint ipepServer = new IPEndPoint(IPAddress.Parse(ip), 0);
                EndPoint epServer = ipepServer;

                int PacketSize = 0;
                IcmpPacket packet = new IcmpPacket();

                packet.Type = ICMP_ECHO; //8
                packet.SubCode = 0;
                packet.CheckSum = UInt16.Parse("0");
                packet.Identifier = UInt16.Parse("45");
                packet.SequenceNumber = UInt16.Parse("0");
                int PingData = 32; // sizeof(IcmpPacket) - 8;

                packet.Data = new Byte[PingData];
                for (int i = 0; i < PingData; i++)
                {
                    packet.Data[i] = (byte)'#';
                }

                PacketSize = PingData + 8;
                Byte[] icmp_pkt_buffer = new Byte[PacketSize];

                Int32 Index = Serialize(packet, icmp_pkt_buffer, PacketSize, PingData);
                if (Index == -1)
                {
                    return false;
                }

                Double double_length = Convert.ToDouble(Index);
                Double dtemp = Math.Ceiling(double_length / 2);

                int cksum_buffer_length = Convert.ToInt32(dtemp);
                UInt16[] cksum_buffer = new UInt16[cksum_buffer_length];
                int icmp_header_buffer_index = 0;

                for (int i = 0; i < cksum_buffer_length; i++)
                {
                    cksum_buffer[i] = BitConverter.ToUInt16(icmp_pkt_buffer, icmp_header_buffer_index);
                    icmp_header_buffer_index += 2;
                }
                UInt16 u_cksum = checksum(cksum_buffer, cksum_buffer_length);
                packet.CheckSum = u_cksum;
                Byte[] sendbuf = new Byte[PacketSize];

                Index = Serialize(packet, sendbuf, PacketSize, PingData);

                if (Index == -1)
                {
                    socket.Close();
                    return false;
                }

                Byte[] ReceiveBuffer = new Byte[256];
                int nBytes = 0;


                ArrayList li = new ArrayList(1);
                li.Add(socket);

                if (socket.SendTo(sendbuf, ipepServer) < 0)
                {
                    socket.Close();
                    return false;
                }


                if (!socket.Poll(500000, SelectMode.SelectRead))
                {
                    socket.Close();
                    return false;
                }

                nBytes = socket.ReceiveFrom(ReceiveBuffer, ref epServer);
                if (nBytes > 0)
                {
                    return true;
                }
            }
            catch (Exception e)
            {
                e.Message.ToString();
            }
            finally 
            {
                try
                {
                    if (socket != null)
                    {
                        socket.Shutdown(SocketShutdown.Both);
                        socket.Close();
                        socket.Dispose();
                    }
                }
                catch (Exception ex) { }
            }
            return false;
        }

        /// <summary>
        ///  This method get the Packet and calculates the total size 
        ///  of the Pack by converting it to byte array
        /// </summary>
        public static Int32 Serialize(IcmpPacket packet, Byte[] Buffer, Int32 PacketSize, Int32 PingData)
        {
            Int32 cbReturn = 0;
            int Index = 0;

            Byte[] b_type = new Byte[1];
            b_type[0] = (packet.Type);

            Byte[] b_code = new Byte[1];
            b_code[0] = (packet.SubCode);

            Byte[] b_cksum = BitConverter.GetBytes(packet.CheckSum);
            Byte[] b_id = BitConverter.GetBytes(packet.Identifier);
            Byte[] b_seq = BitConverter.GetBytes(packet.SequenceNumber);

            Array.Copy(b_type, 0, Buffer, Index, b_type.Length);
            Index += b_type.Length;

            Array.Copy(b_code, 0, Buffer, Index, b_code.Length);
            Index += b_code.Length;

            Array.Copy(b_cksum, 0, Buffer, Index, b_cksum.Length);
            Index += b_cksum.Length;

            Array.Copy(b_id, 0, Buffer, Index, b_id.Length);
            Index += b_id.Length;

            Array.Copy(b_seq, 0, Buffer, Index, b_seq.Length);
            Index += b_seq.Length;

            // copy the data	        
            Array.Copy(packet.Data, 0, Buffer, Index, PingData);
            Index += PingData;
            if (Index != PacketSize/* sizeof(IcmpPacket)  */)
            {
                cbReturn = -1;
                return cbReturn;
            }

            cbReturn = Index;
            return cbReturn;
        }


        /// <summary>
        ///		This Method has the algorithm to make a checksum 
        /// </summary>
        public static UInt16 checksum(UInt16[] buffer, int size)
        {
            Int32 cksum = 0;
            int counter;

            counter = 0;

            while (size > 0)
            {

                UInt16 val = buffer[counter];

                cksum += Convert.ToInt32(buffer[counter]);
                counter += 1;
                size -= 1;
            }

            cksum = (cksum >> 16) + (cksum & 0xffff);
            cksum += (cksum >> 16);
            return (UInt16)(~cksum);
        }
    }
}
